/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE;
using DaveSexton.DocProject.Resources;

namespace DaveSexton.DocProject
{
	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
	public sealed class VSProjectProperties : MarshalByRefObject, IProjectProperties
	{
		#region Public Properties
		public string this[string name]
		{
			[System.Diagnostics.DebuggerHidden]
			get
			{
				try
				{
					return GetValueAsString(Properties.Item(name));
				}
				catch (ArgumentException)
				{
					return null;
				}
			}
			set
			{
				throw new NotSupportedException();
			}
		}
		#endregion

		#region Private / Protected
		internal Properties Properties
		{
			get
			{
				return (forConfiguration)
				? project.ConfigurationManager.ActiveConfiguration.Properties
				: project.Properties;
			}
		}

		private readonly Project project;
		private readonly bool forConfiguration;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="VSProjectProperties" /> class.
		/// </summary>
		internal VSProjectProperties(VSAnyProject project, bool forConfiguration)
		{
			if (project == null)
				throw new ArgumentNullException("project");

			this.project = project.ProjectBase;
			this.forConfiguration = forConfiguration;
		}
		#endregion

		#region Methods
		[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.LinkDemand,
			Flags = System.Security.Permissions.SecurityPermissionFlag.Infrastructure)]
		public override object InitializeLifetimeService()
		{
			return null;
		}

		[CLSCompliant(false)]
		public static string GetValueAsString(Property property)
		{
			if (property == null)
				return null;

			if (property.NumIndices <= 1)
			// In testing, some proerties returned one (1) and some returned zero (0); 
			// but as long as the number wasn't greater than 1, the Value property must 
			// be read directly instead of using get_IndexedValue, which would throw an 
			// exception stating that the property value is not a collection.
			{
				object value = GetValue(property, 0);

				return (value == null) ? null : value.ToString();
			}
			else
			{
				StringBuilder values = new StringBuilder("{");

				for (int p = 0; p < property.NumIndices; p++)
					values.AppendFormat("{0},", GetValue(property, p + 1));

				values.Remove(values.Length - 1, 1);		// remove trailing comma
				values.Append("}");

				return values.ToString();
			}
		}

		[CLSCompliant(false)]
		public static object GetValue(Property property)
		{
			if (property == null)
				throw new ArgumentNullException("property");

			if (property.NumIndices <= 1)
				// In testing, some proerties returned one (1) and some returned zero (0); 
				// but as long as the number wasn't greater than 1, the Value property must 
				// be read directly instead of using get_IndexedValue, which would throw an 
				// exception stating that the property value is not a collection.
				return GetValue(property, 0);
			else
			{
				object[] values = new object[property.NumIndices];

				for (int p = 0; p < values.Length; p++)
					values[p] = GetValue(property, p + 1);

				return values;
			}
		}

		[System.Diagnostics.DebuggerHidden]
		private static object GetValue(Property property, int index)
		{
			try
			{
				return (index == 0) ? property.Value : property.get_IndexedValue(index, 0, 0, 0);
			}
			catch (System.Reflection.TargetParameterCountException ex)
			{
				return "[Error:" + ex.Message + "]";
			}
			catch (System.Runtime.InteropServices.COMException ex)
			{
				return "[Error:" + ex.Message + "]";
			}
		}
		#endregion

		#region ICollection<string> Members
		public int IndexOf(string item)
		{
			if (item == null)
				throw new ArgumentNullException("item");

			int index = -1;

			foreach (Property property in Properties)
			{
				index++;

				if (property != null && item.Equals(property.Name, StringComparison.OrdinalIgnoreCase))
					break;
			}

			return index;
		}

		public string this[int index]
		{
			[System.Diagnostics.DebuggerHidden]
			get
			{
				try
				{
					Property property = Properties.Item(index);

					return property.Name + "=" + GetValueAsString(property);
				}
				catch (ArgumentException)
				{
					return null;
				}
			}
			[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")]
			set
			{
				throw new NotSupportedException(Errors.ReadOnlyCollection);
			}
		}

		void ICollection<string>.Add(string item)
		{
			throw new NotSupportedException(Errors.ReadOnlyCollection);
		}

		void ICollection<string>.Clear()
		{
			throw new NotSupportedException(Errors.ReadOnlyCollection);
		}

		bool ICollection<string>.Remove(string item)
		{
			throw new NotSupportedException(Errors.ReadOnlyCollection);
		}

		[System.Diagnostics.DebuggerHidden]
		public bool Contains(string item)
		{
			try
			{
				return Properties.Item(item) != null;
			}
			catch (ArgumentException)
			{
				return false;
			}
		}

		public void CopyTo(string[] array, int arrayIndex)
		{
			if (array == null)
				throw new ArgumentNullException("array");

			foreach (Property property in Properties)
				array[arrayIndex++] = property.Name + "=" + GetValueAsString(property);
		}

		public int Count
		{
			get
			{
				return Properties.Count;
			}
		}

		public bool IsReadOnly
		{
			get
			{
				return true;
			}
		}
		#endregion

		#region IEnumerable<string> Members
		public IEnumerator<string> GetEnumerator()
		{
			return new PropertyEnumeratorWrapper(Properties.GetEnumerator());
		}
		#endregion

		#region IEnumerable Members
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return Properties.GetEnumerator();
		}
		#endregion

		#region Nested
		private sealed class PropertyEnumeratorWrapper : IEnumerator<string>
		{
			private readonly System.Collections.IEnumerator enumerator;

			public PropertyEnumeratorWrapper(System.Collections.IEnumerator enumerator)
			{
				this.enumerator = enumerator;
			}

			#region IEnumerator<string> Members
			public string Current
			{
				get
				{
					Property property = (Property) enumerator.Current;

					return (property == null) ? null : property.Name + "=" + GetValueAsString(property);
				}
			}
			#endregion

			#region IDisposable Members
			public void Dispose()
			{
				IDisposable disposable = enumerator as IDisposable;

				if (disposable != null)
					disposable.Dispose();
			}
			#endregion

			#region IEnumerator Members
			object System.Collections.IEnumerator.Current
			{
				get
				{
					return enumerator.Current;
				}
			}

			public bool MoveNext()
			{
				return enumerator.MoveNext();
			}

			public void Reset()
			{
				enumerator.Reset();
			}
			#endregion
		}
		#endregion
	}
}
